1 module hip.hipaudio.backend.xaudio.player;
2 
3 version(Windows):
4 version(DirectX):
5 import hip.util.system:getWindowsErrorMessage;
6 import hip.hipaudio.backend.xaudio.source;
7 import hip.hipaudio.backend.xaudio.clip;
8 import hip.audio_decoding.audio;
9 import hip.error.handler;
10 import hip.hipaudio.audio;
11 import core.sys.windows.windef;
12 import directx.xaudio2;
13 
14 extern(Windows)
15 {
16     // import core.sys.windows.objbase; //This would import all the uuid table.
17     enum COINIT {
18         COINIT_APARTMENTTHREADED = 2,
19         COINIT_MULTITHREADED     = 0,
20         COINIT_DISABLE_OLE1DDE   = 4,
21         COINIT_SPEED_OVER_MEMORY = 8
22     }
23     HRESULT CoInitialize(PVOID);
24     HRESULT CoInitializeEx(LPVOID, DWORD);
25 }
26 
27 class HipXAudioPlayer : IHipAudioPlayer
28 {
29     IXAudio2 xAudio;
30     IXAudio2MasteringVoice masterVoice;
31 
32     package immutable(AudioConfig) config;
33 
34     /**
35     *   For getting better debug information, you need to run this application on Visual Studio.
36     *   The debug messages actually appears inside the `Output` window.
37     */
38     public this(AudioConfig cfg)
39     {
40         this.config = cfg;
41 
42         HRESULT hr;
43         version(UWP)
44         {}
45         else
46         {
47             CoInitializeEx(null, COINIT.COINIT_MULTITHREADED);
48             ErrorHandler.assertLazyExit(SUCCEEDED(hr), "CoInitializeEx error:\n\t"~HipXAudioPlayer.getError(hr));
49         } 
50 
51         UINT32 flags;
52         version(HIPREME_DEBUG)
53             flags|= XAUDIO2_DEBUG_ENGINE;
54         
55         hr = XAudio2Create(xAudio, flags, XAUDIO2_DEFAULT_PROCESSOR);
56         ErrorHandler.assertLazyExit(SUCCEEDED(hr), "XAudio2Create error:\n\t"~HipXAudioPlayer.getError(hr));
57 
58 
59         hr = xAudio.CreateMasteringVoice(&masterVoice, cfg.channels,  cfg.sampleRate, 0);
60         ErrorHandler.assertLazyExit(SUCCEEDED(hr), "CreateMasteringVoice error:\n\t"~HipXAudioPlayer.getError(hr));
61 
62         version(HIPREME_DEBUG)
63         {
64             XAUDIO2_DEBUG_CONFIGURATION debugConfig = XAUDIO2_DEBUG_CONFIGURATION(
65                 XAUDIO2_LOG_ERRORS | XAUDIO2_LOG_WARNINGS, XAUDIO2_LOG_ERRORS, true, true, true, true
66             );
67             xAudio.SetDebugConfiguration(&debugConfig);
68         }
69     }
70 
71     public static string getError(HRESULT hr)
72     {
73         switch(hr)
74         {
75             case XAUDIO2_E_INVALID_CALL: return "XAUDIO2_E_INVALID_CALL: An API call or one of its arguments was illegal";
76             case XAUDIO2_E_XMA_DECODER_ERROR: return "XAUDIO2_E_XMA_DECODER_ERROR: The XMA hardware suffered an unrecoverable error";
77             case XAUDIO2_E_XAPO_CREATION_FAILED: return "XAUDIO2_E_XAPO_CREATION_FAILED: XAudio2 failed to initialize an XAPO effect";
78             case XAUDIO2_E_DEVICE_INVALIDATED: return "XAUDIO2_E_DEVICE_INVALIDATED: An audio device became unusable (unplugged, etc)";
79             default: return getWindowsErrorMessage(hr);
80         }
81     }
82     public bool play_streamed(AHipAudioSource src){return src.play_streamed();}
83 
84     public AHipAudioSource getSource(bool isStreamed){return new HipXAudioSource(this);}
85     public IHipAudioClip getClip(){return new HipXAudioClip(new HipAudioDecoder(), HipAudioClipHint(2, 44_100, false, true));}
86 
87     public IHipAudioClip loadStreamed(string audioName, uint chunkSize)
88     {
89         ErrorHandler.assertExit(false, "XAudio Player does not support chunked decoding");
90         return null;
91     }
92     public void updateStream(AHipAudioSource source){}
93 
94     public void onDestroy()
95     {
96         xAudio.Release();
97     }
98     public void update(){}
99 }